﻿using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace Library2015
{
    #region AppContext

    public class AppContext
    {
        public static AppContext Current { get; set; }

        public AppStartOptions StartOptions { get; set; }
    }

    [Serializable]
    public abstract class AppStartOptions
    {
        public AppStartOptions()
        {
            this.Parameters = new List<AppParameter>();
            OnParameterCreating();
        }
        public List<AppParameter> Parameters { get; private set; }

        protected abstract void OnParameterCreating();

        #region For edit convenience

        [EditorBrowsable(EditorBrowsableState.Never)]
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public override string ToString()
        {
            return base.ToString();
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }
        #endregion
    }

    #endregion

    #region AppParameter

    [Serializable]
    public abstract class AppParameter
    {
        static IList<AppParameter> emptyParameters = new List<AppParameter>(0);
        static IDictionary<int, IList<AppParameter>> emptyValueParameters = new Dictionary<int, IList<AppParameter>>(0);

        protected AppParameter()
        {
            this.ExtensionParameters = emptyParameters;
            this.ValueParameters = emptyValueParameters;
        }

        public string Name { get; protected set; }

        //public bool Optional { get; set; }
        /// <summary>
        /// Indicate if the parameter contains value group.
        /// </summary>
        public virtual bool IsGroup { get { return false; } }

        public abstract object Value { get; protected set; }

        public abstract Type Type { get; }

        public bool Loaded { get; protected set; }

        public virtual void SetValue(object value)
        {
            this.Value = value;
            this.Loaded = true;
        }

        public IList<AppParameter> ExtensionParameters { get; protected set; }

        public IDictionary<int, IList<AppParameter>> ValueParameters { get; protected set; }

        #region Create

        static AppParameter<T> Create<T>(string name, bool optional = false)
        {
            //if (this.Parameters.Exists(p => p.Name == name))
            //    throw new InvalidOperationException(String.Format("", name));

            var parameter = new AppParameter<T>(name, optional);
            //Parameters.Add(parameter);
            return parameter;
        }

        public static AppParameter<T> Require<T>(string name)
        {
            return Create<T>(name);
        }

        public static AppParameter<T> Optional<T>(string name)
        {
            return Create<T>(name, true);
        }

        public static AppParameterGroup<T> Multiple<T>(string name)
        {
            var group = new AppParameterGroup<T>(name);
            return group;
        }
        #endregion
    }

    [Serializable]
    public class AppParameter<T> : AppParameter
    {
        public AppParameter(string name, bool optional = false)
        {
            this.Name = name;
            //this.Optional = optional;
        }

        #region Fields

        public Func<T, ValidationResult> Validate = o => { return ValidationResult.Passed; };
        public Func<T, IList<AppParameter>> GetExtensions;
        #endregion

        #region Value Property

        public override object Value { get; protected set; }

        public override Type Type { get { return typeof(T); } }

        public override void SetValue(object value)
        {
            base.SetValue(value);

            if (this.GetExtensions != null)
                this.ExtensionParameters = this.GetExtensions((T)value);
        }
        #endregion

    }

    [Serializable]
    public class AppParameterGroup<T> : AppParameter<IList<T>>
    {
        public AppParameterGroup(string name, bool optional = false)
            : base(name, optional)
        {
            this.ValueParameters = new Dictionary<int, IList<AppParameter>>();
        }

        static IList<T> emptyList = new List<T>(0);
        public IList<T> ValueList
        {
            get
            {
                var list = this.Value as IList<T>;
                return list ?? emptyList;
            }
        }

        public override bool IsGroup
        {
            get
            {
                return true;
            }
        }

        public override void SetValue(object value)
        {
            base.SetValue(value);

            for (int i = 0; i < this.ValueList.Count; i++)
            {
                var extensions = GetItemExtensions(this.ValueList[i]);

                if (extensions != null) this.ValueParameters.Add(i, extensions);
            }
        }

        public Func<T, ValidationResult> ValidateItem = o => { return ValidationResult.Passed; };
        public Func<T, IList<AppParameter>> GetItemExtensions;
    }
    #endregion

    #region ValidationResult

    [Serializable]
    public class ValidationResult
    {
        #region Static invocation

        static ValidationResult passed = new ValidationResult();
        public static ValidationResult Passed
        {
            get { return passed; }
        }

        public static ValidationResult Error(params string[] messages)
        {
            var result = new ValidationResult { ValidationErrors = new List<ValidationError>() };

            foreach (var item in messages)
            {
                result.ValidationErrors.Add(new ValidationError(item));
            }
            return result;
        }
        #endregion

        public bool IsValid { get { return ValidationErrors == null || this.ValidationErrors.Count == 0; } }

        public IList<ValidationError> ValidationErrors { get; private set; }
    }

    [Serializable]
    public class ValidationError
    {
        public ValidationError(string errorMessage, string propertyName = null, object value = null)
        {
            this.Message = errorMessage;
            this.Name = propertyName;
            this.Value = value;
        }

        public object Value { get; private set; }
        /// <summary>
        /// Gets validation error message.
        /// </summary>
        public string Message { get; private set; }
        /// <summary>
        /// Gets name of the invalid property.
        /// </summary>
        public string Name { get; private set; }
    }
    #endregion
}
